Skip to content

feat: add real-time latency indicator and websocket heartbeat#84

Open
Nakshatra480 wants to merge 1 commit intoAOSSIE-Org:mainfrom
Nakshatra480:feature/latency-indicator
Open

feat: add real-time latency indicator and websocket heartbeat#84
Nakshatra480 wants to merge 1 commit intoAOSSIE-Org:mainfrom
Nakshatra480:feature/latency-indicator

Conversation

@Nakshatra480
Copy link

@Nakshatra480 Nakshatra480 commented Feb 13, 2026

Description

This PR adds a real-time connection quality indicator to the trackpad interface. It implements a lightweight heartbeat mechanism to measure the Round-Trip Time (RTT) between the client and the server, helping users diagnose lag or connection issues.

Changes

  • Backend: Added a pong response handler in websocket.ts to return the client's timestamp.
  • Hook Logic: Updated useRemoteConnection to initiate a heartbeat every 3 seconds and calculate latency.
  • UI Enhancement: Added a color-coded Ping display in the ControlBar:
    • Green: < 50ms (Stable)
    • Yellow: 50ms - 150ms (Noticeable lag)
    • Red: > 150ms (High latency)
  • Stability: Ensured all timers are properly cleared on disconnect/unmount to prevent memory leaks.

Verification

  • Verified that the heartbeat stops when the connection is closed.
  • Confirmed the UI updates correctly and resets to --- when disconnected.
  • Checked that no irrelevant code or "Screen Wake Lock" logic was included in the final diff.

Closes #83

Summary by CodeRabbit

  • New Features
    • Live ping latency display in the trackpad control panel ("Ping: {ms}" or "Ping: ---") to monitor connection quality.
  • Improvements
    • Connection now measures and updates latency periodically; latency is shown above controls and layout adjusted accordingly.
    • Copy/Paste shown disabled; buttons use explicit button semantics for consistency.
  • Accessibility
    • Form labels paired with IP/Port inputs for improved screen-reader support.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an application-level ping/pong heartbeat: the frontend hook periodically sends pings and measures RTT as latency, the server responds with pong, latency is exposed from the hook and displayed color-coded in the ControlBar, plus related input validation and accessibility label tweaks.

Changes

Cohort / File(s) Summary
Connection Hook
src/hooks/useRemoteConnection.ts
Adds `latency: number
UI: Trackpad ControlBar
src/components/Trackpad/ControlBar.tsx
Adds latency prop, getLatencyColor(ms) mapping, renders a latency row above controls with color-coded "Ping: Xms", reworks layout to stacked + 6-column grid, sets type="button" on buttons; Copy/Paste disabled.
Route Propagation
src/routes/trackpad.tsx
Destructures latency from useRemoteConnection() and passes latency={latency} into ControlBar.
Server Ping Handling
src/server/websocket.ts
Handles incoming { type: 'ping' } messages and immediately replies with { type: 'pong', timestamp } before other message processing.
Input Validation
src/server/InputHandler.ts
Adds Number.isFinite checks for numeric fields (dx, dy, delta) before processing scroll/zoom inputs.
Accessibility
src/routes/settings.tsx
Adds htmlFor/id pairings for IP and Port inputs to improve label association.
Types & Gesture Hook
src/types.tsx, src/hooks/useTrackpadGesture.ts
Introduces RemoteMessage union type and updates useTrackpadGesture to accept send: (msg: RemoteMessage) => void (type import).

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client Browser
    participant Hook as useRemoteConnection Hook
    participant WS as WebSocket Server
    participant UI as ControlBar

    Browser->>Hook: Render TrackpadPage / open hook
    Hook->>WS: Open WebSocket
    Note over Hook: start heartbeat (~3s interval)

    loop Heartbeat interval
        Hook->>WS: {"type":"ping","timestamp":T0}
        WS->>Hook: {"type":"pong","timestamp":T0}
        Hook->>Hook: compute RTT = now - T0
        Hook->>UI: update latency prop
        UI->>UI: render "Ping: Xms" with color
    end

    Browser->>Hook: Unmount / Disconnect
    Hook->>Hook: clear timer, close ws, cleanup
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I sent a ping across the lawn,

A pong hopped back before the dawn,
Green for nimble, yellow, red for lag,
My whiskers tap a tiny flag,
A heartbeat hums — go hop, go on!

🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Most changes align with issue #83 objectives; however, the PR includes additional enhancements beyond core requirements: accessibility improvements in settings.tsx, input validation in InputHandler.ts, and type safety additions in types.tsx and useTrackpadGesture.ts. Clarify whether accessibility and type-safety enhancements were intentionally included as scope expansion or if they should be separated into distinct PRs.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add real-time latency indicator and websocket heartbeat' directly and clearly summarizes the main changes: implementing real-time latency feedback through a WebSocket heartbeat mechanism.
Linked Issues check ✅ Passed The pull request fully implements all coding requirements from issue #83: heartbeat mechanism in useRemoteConnection.ts, pong response handler in websocket.ts, color-coded latency display in ControlBar.tsx, and proper cleanup of timers on disconnect/unmount.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/components/Trackpad/ControlBar.tsx`:
- Around line 73-116: The grid is defined as "grid-cols-5" but there are six
active buttons in ControlBar (the Scroll/Cursor button using onToggleScroll,
Copy, Paste, R-Click using onRightClick, Modifier using
getModifierButtonClass/getModifierLabel and onModifierToggle, and Keyboard using
onKeyboardToggle), causing wrapping; update the grid to "grid-cols-6" (or reduce
buttons to five) so all buttons fit on one row—adjust the className on the
container div accordingly.
- Around line 73-116: Every <button> in ControlBar.tsx lacks an explicit type
and therefore defaults to type="submit"; update each button element (the
Scroll/Cursor toggle that calls handleInteraction with onToggleScroll, the Copy
and Paste buttons, the R-Click button that calls handleInteraction with
onRightClick, the modifier button that uses
getModifierButtonClass/getModifierLabel and onModifierToggle, and the Keyboard
button that calls onKeyboardToggle) to include type="button" to prevent
accidental form submission.

In `@src/hooks/useRemoteConnection.ts`:
- Around line 55-59: The cleanup closure currently captures the stale ws state
(initially null) so ws?.close() is never called; change the WebSocket storage to
a ref (e.g., wsRef via useRef<WebSocket | null>) and in connect() assign
wsRef.current = socket (and update any other ws usages to read wsRef.current),
then in the cleanup return call wsRef.current?.close() alongside clearing
reconnectTimer and heartbeatTimer (and null out wsRef.current on close) so the
real socket is reliably closed on unmount.
🧹 Nitpick comments (2)
src/hooks/useRemoteConnection.ts (1)

21-28: Heartbeat fires immediately after reconnect — consider a brief delay.

When the WebSocket reconnects (after the 3s reconnect timeout), the first heartbeat ping fires immediately in onopen, then every 3 seconds via setInterval. This is fine functionally, but if you want the first latency reading sooner after connect, you could send an initial ping immediately in onopen before starting the interval.

This is a minor UX observation — the first latency display won't appear until up to 3 seconds after connection.

Optional: send initial ping immediately
 socket.onopen = () => {
     setStatus('connected');
+    // Send first ping immediately for quick latency reading
+    socket.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
     heartbeatTimer = setInterval(() => {
         if (socket.readyState === WebSocket.OPEN) {
             socket.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
         }
     }, 3000);
 };
src/components/Trackpad/ControlBar.tsx (1)

80-97: Non-functional Copy/Paste buttons and commented-out L-Click code.

The Copy and Paste buttons (lines 80–89) have no event handlers — they render but do nothing on interaction. The L-Click button (lines 90–97) is fully commented out. If these are WIP placeholders, consider removing them from this PR to keep the diff focused on the latency feature, or add TODO comments indicating they're planned.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/components/Trackpad/ControlBar.tsx`:
- Around line 81-92: The Copy and Paste buttons in the ControlBar component are
rendered without handlers so they are non-functional; either wire them up or
mark them disabled for now. Locate the ControlBar component (buttons with text
"Copy" and "Paste") and either add onClick handlers that call the appropriate
functions (e.g., copySelection, pasteClipboard or existing methods in the same
component) or set the buttons to non-interactive by adding the disabled
attribute and a CSS class for reduced opacity/aria-disabled for accessibility.
Ensure any new handlers use existing clipboard utilities or component methods
and keep ARIA attributes consistent.

In `@src/hooks/useRemoteConnection.ts`:
- Around line 4-5: The ws state (useState WebSocket | null) is dead — remove the
unused state and its updater to avoid needless re-renders: delete the const [ws,
setWs] = useState<WebSocket | null>(null); declaration in the
useRemoteConnection hook and remove the setWs(socket) call where the WebSocket
is assigned (leaving wsRef.current = socket and all send/sendCombo/cleanup logic
using wsRef intact).
🧹 Nitpick comments (1)
src/hooks/useRemoteConnection.ts (1)

31-41: RTT calculation assumes msg.timestamp is echoed back as a number.

If the server-side handler ever coerces the timestamp to a string (e.g., via JSON key handling), Date.now() - msg.timestamp would still work due to JS implicit coercion, but a negative or nonsensical RTT could surface if the field is missing or malformed. Consider a guard:

 if (msg.type === 'pong') {
-    const rtt = Date.now() - msg.timestamp;
-    setLatency(rtt);
+    const rtt = Date.now() - msg.timestamp;
+    if (Number.isFinite(rtt) && rtt >= 0) {
+        setLatency(rtt);
+    }
 }

@Nakshatra480 Nakshatra480 marked this pull request as draft February 13, 2026 20:07
@Nakshatra480 Nakshatra480 marked this pull request as ready for review February 14, 2026 21:00
@Nakshatra480 Nakshatra480 marked this pull request as draft February 16, 2026 17:07
@Nakshatra480 Nakshatra480 marked this pull request as ready for review February 16, 2026 18:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/hooks/useRemoteConnection.ts (2)

58-65: Consider exponential backoff for reconnection.

The fixed 3-second reconnect delay (Line 64) could produce rapid repeated connection attempts if the server is down for an extended period. For a LAN-only tool this is low-risk, but an exponential backoff (e.g., 1s → 2s → 4s → capped at 30s) would be more resilient.


91-95: Consider typing msg parameter instead of any.

send accepts any, which bypasses type safety. Since the hook is used to send known message shapes (InputMessage, ping, etc.), a union type or generic would catch misuse at compile time.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/hooks/useRemoteConnection.ts`:
- Around line 103-112: The client sends messages with type "combo" from
sendCombo but RemoteMessage in src/types.tsx lacks that variant; add a new union
member {| type: "combo"; keys: string[] |} to RemoteMessage so it matches the
server-side InputMessage and satisfies TypeScript checks (update any
imports/usages of RemoteMessage if needed, e.g., where sendCombo or WebSocket
message typing is validated). Ensure the added variant follows the same naming
and shape used in sendCombo so serialization/handlers remain consistent.
🧹 Nitpick comments (2)
src/hooks/useRemoteConnection.ts (2)

36-45: Consider clearing heartbeatTimer before starting a new interval.

If onopen were ever invoked more than once for the same socket (unlikely but possible with some runtimes), the previous interval would leak. A defensive clearInterval costs nothing.

Proposed fix
 socket.onopen = () => {
     if (!isMounted) return;
     setStatus("connected");
     reconnectDelay = 1000;
+    clearInterval(heartbeatTimer);
     heartbeatTimer = setInterval(() => {

23-29: onmessage not nulled alongside the other handlers before close().

Lines 23–29 and 87–93 null onopen, onclose, and onerror but skip onmessage. This is inconsistent — while unlikely to cause real issues (the socket is being closed immediately after), nulling it keeps the pattern uniform and prevents any edge-case message delivery during the closing handshake.

Proposed fix
 if (wsRef.current) {
     wsRef.current.onopen = null;
     wsRef.current.onclose = null;
     wsRef.current.onerror = null;
+    wsRef.current.onmessage = null;
     wsRef.current.close();
     wsRef.current = null;
 }

Apply in both locations (lines 23–29 and 87–93).

Also applies to: 87-93

@Nakshatra480 Nakshatra480 marked this pull request as draft February 16, 2026 19:18
@Nakshatra480 Nakshatra480 force-pushed the feature/latency-indicator branch from 8560270 to eb866de Compare February 16, 2026 20:07
@Nakshatra480 Nakshatra480 marked this pull request as ready for review February 16, 2026 20:08
@Nakshatra480
Copy link
Author

Nakshatra480 commented Feb 16, 2026

Hi @imxade , i have added a latency indicator and websocket heartbeat feature. Can you pls review this PR.
Thanks for your time


const getLatencyColor = (ms: number) => {
if (ms < 50) return "text-success";
if (ms < 150) return "text-warning";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nakshatra480 can You add video for this covering all cases , means when connected ping is lowest , ping is high when network is slow , this can be same as Ping representation in BGMI or PUBG, so it would be great if you can produce like that
I hope you are getting this what I mean to say , also you can use Wifi-signal icon to indicate the connection strength

- Add ping/pong heartbeat with 3s interval and immediate first ping
- Show color-coded latency in ControlBar (green/yellow/red)
- Use wsRef for reliable cleanup on unmount
- Exponential backoff for reconnection (1s-30s)
- Null all handlers (incl. onmessage) before closing sockets
- Add RemoteMessage type, type send() and useTrackpadGesture
- Add type="button" to all buttons, disable placeholder Copy/Paste
- Use grid-cols-6 to fit all visible buttons

Closes AOSSIE-Org#83
@Nakshatra480 Nakshatra480 force-pushed the feature/latency-indicator branch from eb866de to 15e57bd Compare February 17, 2026 12:28
@aniket866
Copy link
Contributor

video is requested?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add Connection Latency (Ping) Indicator and Heartbeat Mechanism

2 participants